package com.cac.demo.util.adapter;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.lang.Assert;
import com.cac.yiyan.common.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @className: IdGenerator
 * @description: （描述这个类的作用）
 * @author:
 * @date: 2024/6/22 11:19:19 周六
 * @version: 1.0
 */
public class IdGenerator {
    private static final IdGenerator.Sequence SEQUENCE;

    public IdGenerator() {
    }

    public static Long nextId() {
        return SEQUENCE.nextId();
    }

    public static String nextUUID() {
        return IdGenerator.UUIDGenerator.generate();
    }

    static {
        SEQUENCE = IdGenerator.Sequence.INSTANCE;
    }

    private static class UUIDGenerator {
        private static final int IP;
        private static short counter;
        private static final int JVM;

        private UUIDGenerator() {
        }

        public static String generate() {
            return (new StringBuilder(32)).append(format(getIp())).append(format(getJvm())).append(format(getHiTime())).append(format(getLoTime())).append(format(getCount())).toString();
        }

        private static final String format(int intval) {
            String formatted = Integer.toHexString(intval);
            StringBuilder buf = new StringBuilder("00000000");
            buf.replace(8 - formatted.length(), 8, formatted);
            return buf.toString();
        }

        private static final String format(short shortval) {
            String formatted = Integer.toHexString(shortval);
            StringBuilder buf = new StringBuilder("0000");
            buf.replace(4 - formatted.length(), 4, formatted);
            return buf.toString();
        }

        private static final int getJvm() {
            return JVM;
        }

        private static final short getCount() {
            Class var0 = IdGenerator.UUIDGenerator.class;
            synchronized(IdGenerator.UUIDGenerator.class) {
                if (counter < 0) {
                    counter = 0;
                }

                short var10000 = counter;
                counter = (short)(var10000 + 1);
                return var10000;
            }
        }

        private static final int getIp() {
            return IP;
        }

        private static final short getHiTime() {
            return (short)((int)(System.currentTimeMillis() >>> 32));
        }

        private static final int getLoTime() {
            return (int)System.currentTimeMillis();
        }

        private static final int toInt(byte[] bytes) {
            int result = 0;
            int length = 4;

            for(int i = 0; i < length; ++i) {
                result = (result << 8) - -128 + bytes[i];
            }

            return result;
        }

        static {
            int ipadd;
            try {
                ipadd = toInt(InetAddress.getLocalHost().getAddress());
            } catch (Exception var2) {
                ipadd = 0;
            }

            IP = ipadd;
            counter = 0;
            JVM = (int)(System.currentTimeMillis() >>> 8);
        }
    }

    private static class Sequence {
        private static final Logger logger = LoggerFactory.getLogger(IdGenerator.Sequence.class);
        private static final IdGenerator.Sequence INSTANCE = new IdGenerator.Sequence();
        private final long twepoch = 1288834974657L;
        private final long workerIdBits = 5L;
        private final long datacenterIdBits = 5L;
        private final long maxWorkerId = 31L;
        private final long maxDatacenterId = 31L;
        private final long sequenceBits = 12L;
        private final long workerIdShift = 12L;
        private final long datacenterIdShift = 17L;
        private final long timestampLeftShift = 22L;
        private final long sequenceMask = 4095L;
        private final long workerId;
        private final long datacenterId;
        private long sequence = 0L;
        private long lastTimestamp = -1L;
        private InetAddress inetAddress;

        private Sequence() {
            this.datacenterId = this.getDatacenterId(31L);
            this.workerId = this.getMaxWorkerId(this.datacenterId, 31L);
        }

        private Sequence(InetAddress inetAddress) {
            this.inetAddress = inetAddress;
            this.datacenterId = this.getDatacenterId(31L);
            this.workerId = this.getMaxWorkerId(this.datacenterId, 31L);
        }

        private Sequence(long workerId, long datacenterId) {
            Assert.isFalse(workerId > 31L || workerId < 0L, String.format("worker Id can't be greater than %d or less than 0", 31L), new Object[0]);
            Assert.isFalse(datacenterId > 31L || datacenterId < 0L, String.format("datacenter Id can't be greater than %d or less than 0", 31L), new Object[0]);
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }

        protected long getMaxWorkerId(long datacenterId, long maxWorkerId) {
            StringBuilder mpid = new StringBuilder();
            mpid.append(datacenterId);
            String name = ManagementFactory.getRuntimeMXBean().getName();
            if (StringUtil.hasText(name)) {
                mpid.append(name.split("@")[0]);
            }

            return (long)(mpid.toString().hashCode() & '\uffff') % (maxWorkerId + 1L);
        }

        protected long getDatacenterId(long maxDatacenterId) {
            long id = 0L;

            try {
                if (null == this.inetAddress) {
                    this.inetAddress = InetAddress.getLocalHost();
                }

                NetworkInterface network = NetworkInterface.getByInetAddress(this.inetAddress);
                if (null == network) {
                    id = 1L;
                } else {
                    byte[] mac = network.getHardwareAddress();
                    if (null != mac) {
                        id = (255L & (long)mac[mac.length - 2] | 65280L & (long)mac[mac.length - 1] << 8) >> 6;
                        id %= maxDatacenterId + 1L;
                    }
                }
            } catch (Exception var7) {
                logger.warn(" getDatacenterId: " + var7.getMessage());
            }

            return id;
        }

        public synchronized long nextId() {
            long timestamp = this.timeGen();
            if (timestamp < this.lastTimestamp) {
                long offset = this.lastTimestamp - timestamp;
                if (offset > 5L) {
                    throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
                }

                try {
                    this.wait(offset << 1);
                    timestamp = this.timeGen();
                    if (timestamp < this.lastTimestamp) {
                        throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
                    }
                } catch (Exception var6) {
                    throw new RuntimeException(var6);
                }
            }

            if (this.lastTimestamp == timestamp) {
                this.sequence = this.sequence + 1L & 4095L;
                if (this.sequence == 0L) {
                    timestamp = this.tilNextMillis(this.lastTimestamp);
                }
            } else {
                this.sequence = ThreadLocalRandom.current().nextLong(1L, 3L);
            }

            this.lastTimestamp = timestamp;
            return timestamp - 1288834974657L << 22 | this.datacenterId << 17 | this.workerId << 12 | this.sequence;
        }

        protected long tilNextMillis(long lastTimestamp) {
            long timestamp;
            for(timestamp = this.timeGen(); timestamp <= lastTimestamp; timestamp = this.timeGen()) {
            }

            return timestamp;
        }

        protected long timeGen() {
            return SystemClock.now();
        }
    }
}
